package com.rapier.sso.server.controller;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSONObject;
import com.rapier.sso.constant.RapierComConstant;
import com.rapier.sso.constant.RapierWebConstant;
import com.rapier.sso.core.config.redis.RedisClient;
import com.rapier.sso.core.entity.TDbSsoClientEntity;
import com.rapier.sso.core.model.ClientSystem;
import com.rapier.sso.core.model.Credential;
import com.rapier.sso.core.service.IClientSystemService;
import com.rapier.sso.core.store.RapierClientsPool;
import com.rapier.sso.model.ReturnModel;
import com.rapier.sso.model.UserModel;
import com.rapier.sso.server.constant.RapierServerConstant;
import com.rapier.sso.server.env.RapierSsoServerEnv;
import com.rapier.sso.server.handler.IAuthenticationHandler;
import com.rapier.sso.server.utils.UserModelUtlis;
import com.rapier.sso.utils.CookieUtil;
import com.rapier.sso.utils.ThreadLocalUtil;
import com.rapier.sso.utils.TokenUtils;

/**
 * Login页面Action
 *
 * @author
 */
@Controller
public class SsoServerWebController {

	@Autowired
	private RedisClient redisClient;
	@Autowired
	private RapierSsoServerEnv ssoServerEnv;
	@Autowired
	private IAuthenticationHandler authHandler;
	@Autowired
	private RapierClientsPool clientPool;
	@Autowired
	private IClientSystemService clientSystemService;

	/**
	 * 登录跳转
	 * 
	 * @param backUrl
	 * @param useSsoLogin
	 * @param model
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	@RequestMapping(value = RapierWebConstant.PATH_LOGIN)
	public String login(@RequestParam(value = RapierWebConstant.RAPIER_REDIRECT_URL, required = false) String redirectUrl,
			Model model, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 获取St-Cookie
		String stCookie = CookieUtil.getValue(request, RapierComConstant.RAPIER_SERVICE_TICKET);
		if (StringUtils.isBlank(stCookie)) {// 没有登录
			String tgCookie = CookieUtil.getValue(request, RapierComConstant.RAPIER_TICKET_GRANT);
			if (StringUtils.isBlank(tgCookie)) {// 没有登录痕迹,直接跳转回去
				return authFailed(redirectUrl, request, model);
			} else { // RemeberMe
				UserModel loginUser = authHandler.autoLogin(tgCookie);
				if (loginUser == null) {
					return authFailed(redirectUrl, request, model);
				} else {
					// 格式化
					loginUser = UserModelUtlis.getClientModel(loginUser);
					stCookie = authSuccess(response, loginUser, true);
					return validateSuccess(redirectUrl, stCookie, loginUser, response, model);
				}
			}
		} else { // stCookie存在
			UserModel loginUser = validateSTicket(stCookie);
			if (loginUser != null) { // stCookie有效
				return validateSuccess(redirectUrl, stCookie, loginUser, response, model); // 验证成功后操作
			} else { // stCookie 失效，转入登录页
				return authFailed(redirectUrl, request, model);
			}
		}
	}

	/**
	 * 登录验证
	 * 
	 * @param backUrl
	 * @param rememberMe
	 * @param request
	 * @param session
	 * @param response
	 * @param map
	 * @return
	 * @throws Exception
	 */
	@RequestMapping(value = RapierWebConstant.PATH_DOLOGIN)
	public String login(
			@RequestParam(value = RapierWebConstant.RAPIER_REDIRECT_URL, required = false) String redirectUrl,
			Boolean rememberMe, HttpServletRequest request, HttpServletResponse response, Model model)
			throws Exception {
		Credential credential = new Credential();
		UserModel loginUser = authHandler.authenticate(credential);
		if (loginUser == null) {
			model.addAttribute("errorMsg", credential.getError());
			model.addAttribute(RapierWebConstant.RAPIER_REDIRECT_URL, redirectUrl);
			return getLoginPath(request, model);
		} else {
			String st = authSuccess(response, loginUser, rememberMe);
			return validateSuccess(redirectUrl, st, loginUser, response, model);
		}
	}

	private String getLoginPath(HttpServletRequest request, Model model) {
		ClientSystem clientSystem = ThreadLocalUtil.get(RapierComConstant.RAPIER_CLIENT);
		if (clientSystem == null) {
			return "/login";
		}
		model.addAttribute(RapierComConstant.RAPIER_CLIENT_ID, clientSystem.getClient().getId());
		if (StringUtils.isBlank(clientSystem.getClient().getLoginPage())) {
			return "/login";
		}
		model.addAttribute("baseUrl", request.getScheme() + "://" + request.getServerName() + ":"
				+ request.getServerPort() + request.getContextPath() + "/");
		return clientSystem.getClient().getLoginPage();
	}

	/**
	 * 用户退出
	 * 
	 * @param backUrl
	 * @param request
	 * @param response
	 * @return
	 * @throws IOException
	 */
	@RequestMapping(RapierWebConstant.PATH_LOGOUT)
	public String logout(
			@RequestParam(value = RapierWebConstant.RAPIER_REDIRECT_URL, required = false) String redirectUrl,
			HttpServletRequest request, HttpServletResponse response) throws Exception {
		String serviceticket = CookieUtil.getValue(request, RapierComConstant.RAPIER_SERVICE_TICKET);
		// 清除自动登录信息
		UserModel loginUser = validateSTicket(serviceticket);
		if (loginUser != null) {
			// 清除服务端自动登录状态
			authHandler.clearLoginToken(loginUser);
			// 清除自动登录cookie
			CookieUtil.remove(request, response, RapierComConstant.RAPIER_SERVICE_TICKET);
		}
		// 移除token
		if (loginUser != null) {
			redisClient.remove(RapierServerConstant.REDIS_SSO_USER_ST_KEY + loginUser.getId());
		}
		// 移除server端
		Cookie cookie = new Cookie(RapierComConstant.RAPIER_SERVICE_TICKET, null);
		cookie.setMaxAge(0);
		response.addCookie(cookie);
		// 通知各客户端logout
		for (ClientSystem clientSystem : clientPool.getClients()) {
			clientSystemService.noticeLogout(clientSystem, serviceticket);
		}
		if (StringUtils.isBlank(redirectUrl)) {
			return "redirect:/login";// 进入默认登录页面
		} else {
			return "redirect:" + redirectUrl;
		}
	}

	/**
	 * 验证ServiceTicket
	 * 
	 * @param stCookie
	 * @return
	 */
	private UserModel validateSTicket(String sTicket) {
		if (StringUtils.isBlank(sTicket)) {
			return null;
		}
		String userId = TokenUtils.getUserId(sTicket);
		Object stObj = redisClient.get(RapierServerConstant.REDIS_SSO_USER_ST_KEY + userId);
		if (stObj != null && UserModel.class.isInstance(stObj)) {
			return (UserModel) stObj;
		}
		return null;
	}

	/**
	 * 授权认证失败时返回的内容设置
	 * 
	 * @param redirectUrl
	 * @param notLogin
	 * @param response
	 * @return
	 * @throws IOException
	 */
	private String authFailed(String redirectUrl, HttpServletRequest request, Model model) throws IOException {
		model.addAttribute(RapierWebConstant.RAPIER_REDIRECT_URL, redirectUrl);
		return getLoginPath(request, model);
	}

	// 授权成功后的操作
	private String authSuccess(HttpServletResponse response, UserModel loginUser, Boolean rememberMe) throws Exception {
		// 生成TG
		if (rememberMe != null && rememberMe) {
			String lt = this.authHandler.loginToken(loginUser);
			setTGCookie(lt, response);
		}
		// 存入Redis
		String stCookie = TokenUtils.makeToken(loginUser.getId(), RapierServerConstant.RAPIER_SSO_SERVER_SECRET, 0L);
		redisClient.set(RapierServerConstant.REDIS_SSO_USER_ST_KEY + loginUser.getId(), loginUser,
				ssoServerEnv.getTokenSTExpireSeconds(), TimeUnit.SECONDS);
		// 写 Cookie
		Cookie cookie = new Cookie(RapierComConstant.RAPIER_SERVICE_TICKET, stCookie);
		response.setHeader("P3P",
				"CP=\"CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR\"");
		response.addCookie(cookie);
		return stCookie;
	}

	// VT验证成功或登录成功后的操作
	private String validateSuccess(String redirectUrl, String stCookie, UserModel loginUser,
			HttpServletResponse response, Model model) throws Exception {
		if (StringUtils.isNoneBlank(redirectUrl)) {
			return "redirect:" + appendUrlParameter(redirectUrl, RapierComConstant.RAPIER_SERVICE_TICKET, stCookie);
		} else {
			List<ClientSystem> clientList = clientPool.getClients();
			List<TDbSsoClientEntity> entits = new ArrayList<TDbSsoClientEntity>();
			for(ClientSystem index : clientList) {
				if(index == null || index.getClient() == null || StringUtils.isBlank(index.getClient().getBaseUrl())) {
					continue;
				}
				entits.add(index.getClient());
			}
			model.addAttribute("clientList", clientPool.getClients());
			model.addAttribute(RapierComConstant.RAPIER_SERVICE_TICKET, stCookie);
			model.addAttribute("userInfo", loginUser);
			return "welcome";
		}
	}

	// 写tgcookie
	private void setTGCookie(String token, HttpServletResponse response) {
		Cookie ltCookie = new Cookie(RapierComConstant.RAPIER_TICKET_GRANT, token);
		ltCookie.setMaxAge(this.ssoServerEnv.getTokenTGExpireHours() * 60 * 60);
		response.addCookie(ltCookie);
	}

	/**
	 * verify
	 *
	 * @param serviceticket
	 * @param clientId
	 * @return
	 */
	@RequestMapping(value = RapierWebConstant.PATH_VERIFY, method = RequestMethod.POST)
	@ResponseBody
	public ReturnModel<UserModel> verify(@RequestBody JSONObject json) {
		UserModel model = validateSTicket(json.getString(RapierComConstant.RAPIER_SERVICE_TICKET));
		if (model != null) {
			return new ReturnModel<UserModel>(model);
		}
		return new ReturnModel<UserModel>(ReturnModel.FAIL_CODE, null);
	}

	/**
	 * 向url后追加参数，拼接时需要判断连接符是? or &，同时需要对参数值进行编码
	 * 
	 * @param origUrl
	 * @param parameterName
	 * @param parameterVal
	 * @return
	 */
	public static String appendUrlParameter(String origUrl, String parameterName, String parameterVal) {
		if (origUrl == null) {
			return null;
		}
		String bound = origUrl.contains("?") ? "&" : "?";
		try {
			return origUrl + bound + parameterName + "=" + URLEncoder.encode(parameterVal, "utf-8");
		} catch (Exception e) {
			return null;
		}
	}

}